﻿using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;

namespace XDQ.VAT.Lockers
{
    public class AbpDistributeLocker : IDistributeLocker, ITransientDependency
    {
        public AbpDistributeLocker(IDistributedCache<MyLocker> distributedCache, ILogger<AbpDistributeLocker> logger)
        {
            DistributedCache = distributedCache;
            Logger = logger;
        }

        public IDistributedCache<MyLocker> DistributedCache { get; }
        public ILogger<AbpDistributeLocker> Logger { get; }

        protected virtual string GetKey<TMainEntity>() => $"locker-{typeof(TMainEntity).FullName}";

        public async Task<bool> Release<TMainEntity>()
        {
            string key = GetKey<TMainEntity>();
            try
            {
                await DistributedCache.RemoveAsync(key, false);
                return true;
            }
            catch (Exception exp)
            {
                Logger.LogError(exp, $"无法释放{key}");
                return false;
            }
        }

        public async Task<bool> TryLock<TMainEntity>()
        {
            string key = GetKey<TMainEntity>();
            try
            {
                var locker = await DistributedCache.GetAsync(key, false);
                if (locker != null) return false;
                await DistributedCache.SetAsync(key, new MyLocker<TMainEntity>(), new DistributedCacheEntryOptions()
                {
                    SlidingExpiration = TimeSpan.FromMinutes(5)
                }, false);
                return true;
            }
            catch (Exception exp)
            {
                Logger.LogError(exp, $"无法锁定{key}");
                return false;
            }
        }

        public Task<bool> TryLock<TMainEntity>(TimeSpan wait)
        {
            return TryLock<TMainEntity>();
        }
    }

    public class MyLocker
    {
        internal MyLocker(string key)
        {
            this.Key = key;
        }


        public string Key { get; set; }
    }

    public class MyLocker<TMainEntity> : MyLocker
    {
        internal MyLocker() : base(typeof(TMainEntity).Name) { }
    }
}
